#pragma warning( disable: 4100 )
#define _WIN32_WINNT 0x0600 
#define _VISTA_THREAD_POOL_

#include <functional>
#include <windows.h>

/*
    A wrapper for working callback.
*/
template<class TContext> 
    class CWorkCallBack
{
    private:
        VOID (*m_pfn)(TContext); //Function pointer
        TContext m_context;     // Call back parameter

    public:
        CWorkCallBack(VOID (*pfn)(TContext),TContext context) :
          m_pfn(pfn),
          m_context(context)
       {
       }

        VOID DoWork()
        {
            (*m_pfn)(m_context);
        }

    static VOID CALLBACK CallBackFunc(
             PTP_CALLBACK_INSTANCE Instance,
             PVOID Context,
             PTP_WORK Work)
    {
        CWorkCallBack *wc=reinterpret_cast<CWorkCallBack*>(Context);
        wc->DoWork();
    }

     static VOID CALLBACK TimerCallBack(
             PTP_CALLBACK_INSTANCE Instance,
             PVOID Context,
             PTP_TIMER timer)
    {
        CWorkCallBack *wc=reinterpret_cast<CWorkCallBack*>(Context);
        wc->DoWork();
    }
};

/*
    A wrapper for Vista Thread Pool.
*/
class CVistaThreadPool
{
protected:
    PTP_POOL m_pool;
    DWORD m_MaxThreads;
    DWORD m_MinThreads;
    volatile long m_TotalWorkItem;
    volatile long m_PendingWorkItem;
    TP_CALLBACK_ENVIRON m_TPENV; 
    CVistaThreadPool(DWORD nMinThreads, DWORD nMaxThreads=8);
    friend class CTimer;

    template<class TContext>
    PTP_WORK QueueWorkerItemInternal(CWorkCallBack<TContext> *item, BOOL bCloseAfterSubmit)
    {
        PTP_WORK pwk=CreateThreadpoolWork(&(CWorkCallBack<TContext>::CallBackFunc),item,&m_TPENV);
        if (pwk==NULL)
        {
            goto cleanup;
        }
        
        //Allocate the work successfully. Increase the work item number here
        InterlockedIncrement(&this->m_TotalWorkItem);

        SubmitThreadpoolWork(pwk);

        if (bCloseAfterSubmit)
        {
            CloseThreadpoolWork(pwk);
            pwk=NULL;
        }
        
        return pwk;

    cleanup:
        return NULL;
    }

    

public:
    static CVistaThreadPool *CreateThreadPool(DWORD nMinThreads,DWORD nMaxThreads=8)
    {
        CVistaThreadPool *p_TP=new CVistaThreadPool(nMaxThreads,nMinThreads);
        if (p_TP->m_pool!=NULL)
            return p_TP;
        else
            return NULL;
    }

    template<class TContext>
        BOOL QueueWorkerItem(CWorkCallBack<TContext> *item)
    {
        QueueWorkerItemInternal(item,TRUE);
        return TRUE;
    }

    template<class TContext>
    BOOL QueueWorkerItemAndBlock(CWorkCallBack<TContext> *item)
    {
        PTP_WORK pwk=QueueWorkerItemInternal(item,FALSE);

        if (pwk==NULL)
        {
            goto cleanup;
        }

        WaitForThreadpoolWorkCallbacks(pwk,FALSE);

        return TRUE;

cleanup:
        return FALSE;
    }

    DWORD MaxThreads(){return m_MaxThreads;}
    DWORD MinThreads(){return m_MinThreads;}
    DWORD ProcessorCount();
    ~CVistaThreadPool();
};